package com.hexb.core.mybatis


import com.hexb.core.model.enums.IDType
import com.hexb.core.mybatis.entry.ColumnEntry
import com.hexb.core.mybatis.entry.SqlEntry
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator
import org.apache.ibatis.executor.keygen.NoKeyGenerator
import org.apache.ibatis.executor.keygen.SelectKeyGenerator
import org.apache.ibatis.mapping.MappedStatement
import org.apache.ibatis.mapping.ResultMap
import org.apache.ibatis.mapping.SqlCommandType
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
import org.apache.ibatis.session.Configuration
import org.slf4j.Logger
import org.slf4j.LoggerFactory

/**
 * @Package : com.hexb.core.mybatis
 * @Author : hexb 
 * @Date : 2018-08-12 17:56
 */
class SQLInsertBuilder {

    static final Logger logger = LoggerFactory.getLogger(SQLInsertBuilder.class)
    static final String insert = 'insert'
    static final String insertBatch = 'insertBatch'

    static MappedStatement insert(SqlEntry entry, Configuration configuration) {
        def id = "${entry.namespace}.${insert}"
        insert(entry, configuration, id, false)
    }

    static MappedStatement insertBatch(SqlEntry entry, Configuration configuration) {
        def id = "${entry.namespace}.${insertBatch}"
        insert(entry, configuration, id, true)
    }

    static MappedStatement insert(SqlEntry entry, Configuration configuration, String id, boolean batch) {
        try {
            def sql = ["insert into ${entry.tableName} ", batch ? insertBatchSQL(entry, configuration) : insertSQL(entry, configuration)].join(' ')
            def sqlSource = new XMLLanguageDriver().createSqlSource(configuration, "<script>${sql}</script>", entry.entryClass)
            def msBuilder = new MappedStatement.Builder(configuration, id,
                    sqlSource,
                    SqlCommandType.INSERT)

            msBuilder.keyColumn(entry.idEntry.column)
            msBuilder.keyProperty(entry.idEntry.property)
            if (entry?.idEntry.idType != IDType.NO_KEY) {
                msBuilder.keyGenerator(entry.idEntry.idType == IDType.UUID && !batch
                        ? createSelectKeyGenerator(configuration, entry, id)
                        : new Jdbc3KeyGenerator())
            } else {
                msBuilder.keyGenerator(new NoKeyGenerator())
            }
            msBuilder.build()
        } catch (e) {
            logger.error(e)
            throw e
        }
    }

    private static SelectKeyGenerator createSelectKeyGenerator(Configuration configuration, SqlEntry entry, String id) {
        def sql = "select replace(uuid(),'-','') from dual"
        def sqlSource = new XMLLanguageDriver().createSqlSource(configuration, "<script>${sql}</script>", String.class)
        def mappedStatement = new MappedStatement.Builder(configuration, "$id!${SelectKeyGenerator.SELECT_KEY_SUFFIX}",
                sqlSource,
                SqlCommandType.SELECT)
                .keyProperty(entry.idEntry.property)
                .resultMaps([new ResultMap.Builder(configuration, "$id!${SelectKeyGenerator.SELECT_KEY_SUFFIX}-Inline", String.class, [], true).build()])
                .build()
        new SelectKeyGenerator(mappedStatement, true)
    }


    private static String insertSQL(SqlEntry entry, Configuration configuration) {
        def columns = insertColumns(entry)
        def columnSQL = columns.collect {
            """<if test="${it.property}!= null">`${it.column}`,</if>"""
        }.join('')
        columnSQL = """<trim suffixOverrides=",">$columnSQL</trim>"""

        def valueSQL = columns.collect {
            """<if test="${it.property}!=null">#{${it.property}},</if>"""
        }.join('\n')
        valueSQL = """<trim suffixOverrides=",">$valueSQL</trim>"""
        "  (  $columnSQL ) values (  $valueSQL )  "
    }


    private static String insertBatchSQL(SqlEntry entry, Configuration configuration) {
        def columns = insertColumns(entry)
        def columnSQL = columns.collect { it.column }.join(' , ')
        def valueSQL = columns.collect {
            it.column == entry.idEntry.column ? """ replace(uuid(),'-','') """ : "#{item.${it.property}}"
        }.join(',')
        valueSQL = """ <foreach collection="list" separator="," item="item">($valueSQL)</foreach>"""
        "( $columnSQL ) values $valueSQL "
    }


    private static List<ColumnEntry> insertColumns(SqlEntry entry) {
        //UUID/NO_KEY排除insertIgnore注解字段
        //自增ID排除Id字段和InsertIgnore注解字段
        def columns = entry.idEntry.idType == IDType.UUID || entry.idEntry.idType == IDType.NO_KEY ?
                entry.propertyMapping.findAll { !it.insertIgnore || it.column == entry.idEntry.column } :
                entry.propertyMapping.findAll { it.column != entry.idEntry.column && !it.insertIgnore }
        columns
    }

}
